package org.gap.jseed;

import static org.gap.jseed.JavaWriter.*;

import org.gap.jseed.util.ClassUtil;

import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;

public abstract class AbstractInjector {

	public abstract void injectBehavior(Class<?> interfaceType, CtClass type, CtClass implementation) 
			throws ClassNotFoundException, CannotCompileException, NotFoundException;

	protected void injectMethod(CtClass implementation, CtMethod method, String body) throws CannotCompileException {
		createMethodWithBody(implementation, method, body);
	}

	protected void injectDefaultMethod(CtClass implementation, CtMethod method) throws CannotCompileException, NotFoundException {
		String body = writeDefaultMethodBodyFor(method);
		createMethodWithBody(implementation, method, body);
	}

	private String writeDefaultMethodBodyFor(CtMethod method) throws NotFoundException {
		return braces(
					line(returnCall(method, defaultValue(method.getReturnType())))
				);
	}

	public void createMethodWithBody(CtClass implementation, CtMethod method, String body) throws CannotCompileException {
		CtMethod newMethod = new CtMethod(method, implementation, null);
		newMethod.setBody(body);
		implementation.addMethod(newMethod);
	}

	/**
	 * Injects the new field into the class.
	 * @param subject
	 * @param fieldClass
	 * 
	 * @throws CannotCompileException
	 * @throws NotFoundException
	 */
	protected void injectField(String fieldName, CtClass subject, Class<?> fieldClass) throws CannotCompileException, NotFoundException {
		if (!ClassUtil.hasField(subject, fieldName)) {
			String fieldString = writeFieldDeclaration(fieldName, fieldClass);
			CtField field = CtField.make(fieldString, subject);
			field.setModifiers(Modifier.PRIVATE);
			subject.addField(field);
		}
	}

	private String writeFieldDeclaration(String fieldName, Class<?> fieldClass) {
		return fieldClass.getName() + " " + fieldName + " = new " + fieldClass.getName() + "();";
	}
}
